Преобразования процедур

        Все функции, рассматриваемые в этом разделе, находятся в пакете codegen. Функция makeproc из этого же пакета была рассмотрена в предыдущей лекции. Здесь описываются средства, позволяющие объединять процедуры (joinprog), оптимизировать процедуры и выражения (optimize, cost), преобразовывать отдельные элементы процедур или менять их свойства (packparams, packargs, packlocals, swapargs, renamevar, makeparam, makeglobal, declare) и защищать процедуры от модификации (protect, unprotect, protected).

Объединение процедур

        Функция joinproc([pro1, pro2, …], result_type = a). Здесь: pro1, pro2, … – процедуры, result_type = a (a Î {seq, list, array}) – необязательная опция. По команде создается новая процедура R, тело которой получается соединением последовательных кодов процедур pro1, pro2, … в порядке их следования. Формальные параметры R являются объединением формальных параметров процедур pro1, pro2, … Аналогично формируются и разделы локальных и глобальных переменных. Пусть все процедуры pro1, pro2, … возвращают результаты вычислений в своих последних предложениях. Тогда по R эти же результаты возвращаются в виде последовательности (a = seq), последовательность списков (a = list) или массива (a = array). Соединение процедур может быть полезным для дальнейшей оптимизации кода R.

        Пример 1. Здесь процедуры ff и gg объединяются в процедуру R. Процедура R выводится и оптимизируется. Оптимизированная процедура Q также выводится. Далее вычисляются значения ff(1, 2), gg(1, 2), R(1, 2), Q(1, 2).

 

> 

restart: with(codegen):

{¿}

> 

ff := proc(x, y) local a, b; a := x^2; b := sin(x); a*b end proc:

{¿}

> 

gg := proc(x, y) local a, c; a := x^2; c := cos(y); a*c end proc:

{¿}

> 

R := joinprocs([ff, gg]);                   # объединение процедур

{¿}

> 

Q := optimize(R);                         # оптимизация процедуры

{¿}

> 

[[ff(1, 2)], [gg(1, 2)], [R(1, 2)], [Q(1, 2)]];

{¿}

Оптимизация процедур и выражений

        Функция optimize(expr) оптимизирует свой аргумент expr. Оптимизация означает, что expr преобразуется к последовательности присваиваний a1 := expr1, a2 := expr2, …, an := exprn так, чтобы при вычислениях значения expr и an совпадали. Имена локальных вспомогательных переменных a1, a2, …, an выбираются системой из множества {t1, t2, …} таким образом, чтобы избежать конфликтов с именами существующих переменных. Если expr процедура, то ее тело должно быть простой последовательностью присваиваний – структуры if или цикла недопустимы.

        Функция optimize(expr, tryhard) выполняется так же, как и первая, но здесь используется иной метод оптимизации, требующий для реализации больше времени и пространства. Однако он, как правило, приводит и к более эффективным решениям.

        Функция cost(expr) подсчитывает количество операций, которые необходимо выполнить при числовых вычислениях expr. Ведется учет сложений и вычитаний (additions), умножений (multiplications), делений (divisions), вызовов функций (functions), вычислений индексов (subscripts) и присваиваний (assignments) при работе с целыми неотрицательными коэффициентами. Если expr – процедура, то подсчитывается и общее количество используемых вспомогательных локальных переменных (storage).

        Пример 2. Здесь подсчитывается количество операций, требуемых для вычисления выражения expr:= 2*x^4+x^3+x без оптимизации и с оптимизацией по функциям optimize.

> 

restart: with(codegen):

{¿}

> 

expr:= 2*x^4+x^3+x: cost(expr);

{¿}

> 

h1:= optimize(expr); cost(h1);

{¿}

> 

optimize(expr, tryhard); cost(%);

{¿}

        Примеры 3. Здесь по процедуре ppp формируются оптимизированные процедуры qqq и rrr. Первая из них создается функцией optimize(expr), а вторая – функцией optimize(expr, tryhard). Для каждой процедуры подсчитывается количество операций выполняемых в ее теле.

> 

restart:

{¿}

> 

ppp := proc(x, m::array(1..3))

{Shift+¿}

 

   m[1] := sin(x); m[2] := x*cos(2*x);

{Shift+¿}

 

   m[3] := x^2*sin(x)+cos(2*x)

{Shift+¿}

 

end proc:

{¿}

> 

codegen[cost](ppp);

{¿}

> 

qqq := codegen[optimize](ppp); codegen[cost](qqq);

{¿}

> 

rrr := codegen[optimize](ppp, tryhard); codegen[cost](rrr);

{¿}

> 

ma := array(1..3): [ppp(z, ma), qqq(z, ma), rrr(z, ma)];

{¿}

Преобразования параметров процедуры (1)

        Функция packparams(pro, li, m) преобразует процедуру pro в новую процедуру, у которой формальные параметры те же самые, что и у pro, но полностью или частично упакованы в одномерный массив m. Размер m равен количеству элементов списка li. В массив m переводятся те формальные параметры pro, имена которых имеются в списке li. Соответственно этому изменяется и тело процедуры pro. В параметрах pro и в переменных li тип указываться не должен.

        Функция packargs(pro, li, m) по выполнению идентична packparams, то есть packparams является синонимом для packargs.

        Функция packlocals(pro, li, m) выполняется аналогично packparams с той разницей, что здесь речь идет не о формальных параметрах, а о локальных переменных процедуры.

        Функция swapargs(pro, n = m) меняет формальные параметры процедуры pro в позициях n и m. Пусть, например, имена этих параметров x и y. В теле процедуры переменные x и y меняются позициями в суммах и произведениях, причем только в подвыражениях вида …+a*x+…+a*y+… и …*x*…*y*…, где выражение a не зависит от x и y.

        Функция renamevar(x = y, pro) изменяет в процедуре pro имя формального параметра, локальной переменной или глобальной переменной с x на y.

        Примеры 4. Здесь демонстрируются следующие преобразования процедуры f: упаковка всех формальных параметров в массив, упаковка части формальных параметров в массив, упаковка локальных переменных в массив, обмен позициями формальных параметров и изменение имен переменных.

> 

restart: with(codegen):

{¿}

> 

f := proc(x, y, z)    # процедура для преобразований

{Shift+¿}

 

   local a, b, c;

{Shift+¿}

 

   a := x*y; b := y+z; c := sin(a+b)

{Shift+¿}

 

end proc:

{¿}

> 

# упаковки формальных параметров в массив

{Shift+¿}

 

g1:= packparams(f, [x, y, z], m);

{¿}

> 

g2 := packparams(f, [x, y], m);

{¿}

> 

# упаковка локальных переменных в массив

{Shift+¿}

 

g3 := packlocals(f, [a, b, c], m);

{¿}

> 

# обмен позициями формальных параметров

{Shift+¿}

 

g4 := swapargs(f, 1 = 2);

{¿}

> 

# изменение имен переменных

{Shift+¿}

 

g5 := renamevar(y = qw, f);

{¿}

Преобразования параметров процедуры (2)

        Функция makeparam(x, pro) из процедуры pro создает новую процедуру R такую, что переменная x из pro становится в R формальным параметром. 

        Функция makeparam(x::ty, pro) из процедуры pro создает новую процедуру R такую, что переменная x из pro становится в R формальным параметром типа ty. Первым аргументом B может быть список вида [x::ty1, y::ty2, …]. В этом случае формальными параметрами в R становятся переменные x, y, … с типами ty1, ty2, … соответственно. В первом элементе списка тип должен быть задан, в остальных элементах это делать не обязательно.

        Функция makeglobal(x, pro) из процедуры pro создает новую процедуру R такую, что переменная x из pro становится в R глобальной. Первым аргументом может быть список вида [x, y, …]. В этом случае глобальными в R становятся все переменные этого списка.

        Функция declare(x::ty, pro) назначает или переназначает тип ty формальному параметру x процедуры pro. Первым аргументом может быть список вида [x::ty1, y::ty2, …]. В этом случае формальным параметрам x, y, … назначаются или переназначаются соответственно типы ty1, ty2, …

        Примеры 5. Здесь демонстрируются следующие преобразования процедуры f предыдущего примера: создание формальных параметров, назначение типа формальным параметрам и создание глобальных переменных.

 

> 

with(codegen):

{¿}

> 

# f  – процедура из предыдущего примера

{Shift+¿}

 

# создание формальных параметров

{Shift+¿}

 

g6 := makeparam([x::posint, y], f);

{¿}

> 

# назначение типа формальным параметрам

{Shift+¿}

 

g7 := declare([x::even, y::odd], g6);

{¿}

> 

# создание глобальных переменных

{Shift+¿}

 

g8 := makeglobal([x, y], f);

{¿}

Защита имен от модификации

        Функция protect(pro1, pro2, … ) защищает любые имена pro1, pro2, … от модификации пользователем или программой. Аргументами могут быть имена простых переменных, массивов, выражений, пользовательских процедур, нейтральных операторов, модулей и т. д. Большая часть системных имен защищена по умолчанию. Например, Pi, sin, sqrt, Zeta и т. д. С переменными окружения функция protect не работает. Защита имен пользовательских процедур предотвращает возможность любого изменения их кода как вручную, так и программным путем. Такая защита действует до ее отмены функцией unprotect или до конца сессии.

        Функция unprotect(pro1, pro2, … )  снимает защиту с любых имен pro1, pro2, … Но вряд ли стоит это делать с системными именами. Их переопределения могут приводить к различным казусам.  

        Функция type(pro, protected) возвращает текущий статус защищенности имени pro в виде значений true или false. Первое из них соответствует защищенному имени, а второе – незащищенному имени. Просмотреть все защищенные имена можно командой select(type, {unames(), anames(anything)}, protectted).

        Примеры 6. Здесь демонстрируются установка защиты и ее снятие с пользовательской процедуры hm.

 

> 

restart:

{¿}

 

hm := proc(x, y)                        

{Shift+¿}

 

   local a;

{Shift+¿}

 

   a := x*y; a^(x-y)

{Shift+¿}

 

end proc:

{¿}

> 

hm := codegen[declare]([x::numeric, y], hm);

{¿}

> 

type(hm, protected);

{¿}

> 

protect(hm):

{Shift+¿}

 

type(hm, protected);

{¿}

> 

hm := codegen[declare]([x::posint, y::posint], hm);

{¿}

> 

unprotect(hm);

{¿}      

Трансляция процедур

из Maple в другие языки программирования

        В пакете CodeGeneretion и его подпакетах имеются средства для трансляции фрагментов Maple-кода и, в частности, процедур и модулей, в языки программирования Matlab, Java, C, VisualBasic и Fortran. На сегодняшний день возможности этих трансляторов ограничены. Лишь в очень простых случаях по ним сразу удается получить из Maple-программы работающую программу на целевом языке. Как правило, в результате трансляции возвращается некий начальный “черновой” код и различные предупреждающие сообщения, призванные помочь пользователю в коррекции полученного кода. Кроме того, в пакете CodeGeneretion имеются команды, позволяющие на базе существующих системных трансляторов создавать новые трансляторы.

Возможности и ограничения трансляции

        При трансляции имена из Maple-кода, не являющиеся ключевыми словами целевого языка, сохраняются в нем без изменений. Имена для неименованных процедур и выражений, а также имена переменных, через которые процедуры возвращают значения, можно задавать опциями resultname и returnvariablename. Из специальных имен трансляторами распознаются лишь Pi, gamma, Catalan, true и false.

        При отсутствии в целевом языке конструкций типа модуля, Maple-модули транслируются в их некоторые эквиваленты. Анализ типов и вывод результатов вычислений для модулей менее надежен, чем для процедур. Не поддерживаются структуры циклов for-in и совместная структура forwhile. По отдельности структуры for и while транслируются правильно. Функциональная форма конструкции if и структура try поддерживается не во всех целевых языках. При множественных присваиваниях левая и правая стороны выражений должны иметь одно и тоже количество членов. Например, x, y := a, b или A := array(1..2): A := [a, b]. Если в целевом языке множественные присваивания не допустимы, то они транслируются в последовательности присваиваний в порядке расположения соответствующих значений. 

        Трансляторы распознают следующие Maple-типы: numeric, float, sfloat, positive, negative, nonpositive, nonnegative, extended_numeric,  integer, posint, negint, nonnegint и nonposint. В этом ряду типы от numeric и до extended_numeric считаются эквивалентными и переводятся в соответствующий вещественный тип с плавающей точкой. Типы от integer и до nonposint также считаются эквивалентными и конвертируются в тип integer. Распознаются также типы массивов array и r-таблиц rtable. Любой из указанных типов можно использовать в опции declare. Комплексные числа не поддерживаются. Другие типы трансляторы распознают в ограниченном контексте. Например, рациональные числа представляются отношениями вещественных чисел с плавающей точкой, строки распознаются внутри сообщений об ошибках и в некоторых вызовах функций, логический тип понимается внутри структур выбора и цикла. Однако в общем случае с этими типами трансляторы не работают.

        Если тип переменной при трансляции распознать не удается, то он автоматически заменяется одним из известных типов и выводится предупреждающее сообщение. Новый тип, по возможности, формируется из контекста Maple-кода, а при неудаче им становится тип, принятый по умолчанию. Создание типа по контексту может приводить к казусам. Например, если нетипированной переменной сначала присваивается целое значение, то ее тип становиться integer. Однако последующие присваивания этой переменной вещественных значений не изменят ее типа. Помощь в правильной конверсии типов могут оказать опции coercetypes, declare, deducetypes и defaulttype. Типирование глобальных переменных при трансляции вообще не проводится.

        При трансляции процедур, содержащих команды return происходит следующее. Типы объектов возвращаемых по return из нескольких точек процедуры должны быть совместимы. Последовательность из двух или более объектов, выводимая по return из одной точки процедуры,  конвертируется в массив. В некоторых ситуациях распознаются неявные возвраты из процедур. Опцией deducereturn = true они могут быть конвертированы в явные возвраты по return. Если это необходимо, возвращаемое значение присваивается автоматически сгенерированной переменной. Ее имя может быть задано опцией returnvariablename. В некоторых языках массивы напрямую не могут возвращаться в качестве результатов вычислений. В этих случаях генерируется возвращаемая переменная. Она добавляется к списку параметров создаваемой функции. Некоторый контроль копирования транслируемых массивов обеспечивается опцией expandarrays. 

        Все трансляторы распознают следующие функции: sin, cos, tan, csc, sec, cot, arcsin, arccos, arctan, arccsc, arcsec, arccot, sinh, cosh, tanh, csch, sech, coth, arcsinh, arccosh, arctanh, arccsch, arcsech, arccoth, abs, exp, ln, log, log10, sqrt, round, trunc, signum, sign, max, min, irem, mod, modp, mods, evalf, evalhf, print, printf. Имеется частичная поддержка функций eval и iquo. При этом каждый конкретный транслятор правильно конвертирует еще некоторый дополнительный набор функций. Нераспознанные имена функций при трансляции не изменяются. Каждая функция транслируется в наиболее подходящий эквивалент целевого языка или его внешних библиотек. Если такого эквивалента нет, то, или подходящий код создается из других средств целевого языка, или генерируется и выводится сообщение об ошибке.

        В отношении массивов в трансляторах реализован ограниченный анализ типов данных, не полностью поддерживаются присваивания и используются упрощенные схемы вывода результатов вычислений. Из Maple-кода в целевые языки можно конвертировать только массивы типа array и rtable (структуры Array, Matrix и Vector). Типы и размеры всех используемых в процедурах массивов рекомендуется объявлять в секциях local и global. Определение и переопределение типов допускается и в опции declare. Инициализация массивов возможна лишь поэлементная или списками. Вложенные списки могут конвертироваться в многомерные массивы, но только если все подсписки одного уровня имеют одинаковую длину. Если для инициализации массива используется список, то его параметры должны соответствовать объявленным размерности и размерам этого массива. Список, обнаруженный в непонятном контексте транслируется без изменений. Если целевой язык не имеет встроенной поддержки копирования массивов, то присваивания массивов реализуются транслятором поэлементно и только для инициализированных элементов. Это, как правило, приводит к необходимости последующей модификации полученного кода. При опции expandarrays = false вместо поэлементного присваивания выполняется формальное присваивание массивов, что также требует дополнительной обработки оттранслированного кода. Множества трансляторами не распознаются.

Оптимизация и иные преобразования Maple-кода

        Для предтрансляционой оптимизации процедур и вычисляемых   последовательностей используется опция optimize. С модулями эта опция не работает. При установке optimize = true запущенный на выполнение транслятор обращается к функции optimize пакета codegen, которая и реализует оптимизацию кода. Такое обращение можно осуществлять и вне трансляторов. В этом случае пользователь получает больший контроль над оптимизационным процессом, вызывая optimize с требуемыми опциями и, при необходимости, внося изменения в полученный оптимизированный код. Заметим, что для преобразований Maple-кода перед трансляцией кроме функции optimize можно использовать и другие средства пакета codegen.

        Для трансляции конкретного Maple-кода могут потребоваться значительные временные и пространственные ресурсы. В случае, когда код является вычисляемой последовательностью (но не процедурой или модулем), сократить время трансляции и уменьшить размер требуемой для этого памяти можно с помощью опции reduceanalysis. 

Общие опции для трансляторов

        В командах трансляции Maple-кода на тот или иной язык программирования в качестве необязательных аргументов могут указываться различные опции. Они призваны помочь процессу трансляции. Ниже дано краткое описание синтаксиса и семантики каждой допустимой опции:

·       coercetypes = lo – разрешение на преобразования типов данных. Здесь lo Î {true, false} и действует опция так. Если lo = true, то при трансляции проводится преобразование типов данных, если lo = false – типы не изменяются. Константы автоматически конвертируются в требуемый тип. Записи coercetypes  и coercetypes = true равносильны. Слово coercion переводится как “приведение данных к какому-либо типу”;

·       declare = [x::ty1, y::ty2, …] – объявление типов переменных в Maple-коде. Здесь x, y, … – имена переменных и ty1, ty2, … – типы. Опция declare определяет или переопределяет типы внутри транслируемого кода. Она используется с процедурами и вычисляемыми последовательностями;

·       deducereturn = lo – задание способа возврата результатов. Здесь lo Î {true, false} и действует опция так. Если lo = true, то все неявно возвращаемые из Maple-кода значения, транслируются в значения, явно возвращаемые по return. Если lo = false, то указанная конверсия результатов вычислений блокируется. Записи deducereturn и deducereturn = true равносильны;

·       deducetypes = lo – назначение типа переменным. Здесь lo Î {true, false} и действует опция так. Если lo = true, то при трансляции тип переменным назначается по существующему контексту Maple-кода. При lo = false тип любых переменных устанавливается по умолчанию (см. опцию defaulttype). Записи deducetypes и deducetypes = true равносильны;

·       defaulttype = tyназначение типа по умолчанию. Опция defaulttype преобразует точно не переводимый транслятором Maple-тип в более широкий тип ty целевого языка. Например, posint ® integer, float ® numeric, symbol ® double и т. д;

·       digits = num – задание количества цифр. Опция digits целым неотрицательным значением num задает количество цифр для вычислений с плавающей точкой по оттранслированной программе. По умолчанию для num принимается значение, установленное в переменной окружения Digits;

·       expandarrays = lo – назначение способа присваивания для массивов. Здесь lo Î {true, false}. Данная опция назначает способ присваивания для массивов при трансляции. Если lo = true, то присваивания вида a := b реализуются поэлементно. Если lo = false, то такие присваивания проводятся формально и после трансляции может потребоваться их дополнительная обработка. Записи expandarrays и expandarrays  = true равносильны;

·       functionprecision = val – установка точности аргументов и возвращаемых значений функций. Здесь val Î {single, double, generic}, причем generic используется только для Фортрана. Опция задает точность представления числовых аргументов функций и возвращаемых ими значений в приемном языке трансляции. Отношения к точности значений переменных опция не имеет;

·       optimize = lo – установка предтрансляционной оптимизации Maple-кода. Здесь lo Î {true, false} и действует опция так. Если lo = true, то перед трансляцией осуществляется оптимизация кода. Опция используется с процедурами и вычисляемыми последовательностями. Записи optimize = true и optimize равносильны;

·       output = val – задание направления вывода. Здесь val Î {name, str, string}, где name – произвольное имя,  str – произвольная строка, string –ключевое слово. Действие данной опции следующее. По умолчанию val = terminal (name), что направляяет вывод данных на терминал. Если name или str даются в качестве значений, то вывод направляется в файл с соответствующим именем. При val = string результат формируется и выводится в виде строки;

·       precision = val – установка точности для переменных. Здесь: val Î {single, double}. Опция precision задает точность представления значений с плавающей точкой для переменных и констант в целевом языке трансляции;

·       reduceanalysis = lo – задание способа анализа кода. Здесь: lo Î {true, false}. Данная опция применяется только для трансляции вычисляемой последовательности, и не может быть использована с процедурами и модулями. Если lo = true, то последовательность анализируется и транслируется поэлементно. Как правило, это сокращает время трансляции и уменьшает размер требуемой памяти. Если lo = false, то последовательность анализируется в целом, во взаимосвязи составляющих ее элементов. Это уменьшает вероятность появления в оттранслированном коде несоответствия типов. Записи reduceanalysis и reduceanalysis = true равносильны;

·       resultname = val – задание имени результата. Данная опция строковым значением val задает имя оттранслированной неименованной процедуры или неименованного выражения. Если опцией имя не задано, то оно автоматически генерируется системой; 

·       returnvariablename = va – задание имени переменной. Данная опция строковым значением va задает имя переменной, через которую транслированная процедура возвращает значение. Это имя используется по мере необходимости. Если опцией имя не определено, то оно автоматически генерируется системой.

Трансляция Maple-кода в язык MATLAB

        Трансляция Maple-кода в язык MATLAB реализуется для версии 6.5. К средствам, распознаваемым всеми трансляторами, в данном случае добавляются функции cat, ceil и floor. При трансляции имена укорачиваются до 31 символа, структура trycatchfinally конвертируется в блок trycatch, число Pi транслируется в константу pi.

        Функция Matlab(eltra, opts) транслирует выражение, список, массив, r-таблицу, процедуру или модуль eltra из Maple в MATLAB. Необязательные аргументы opts – опции, помогающие процессу трансляции. Трансляция проводится следующим образом:

·       если eltra – выражение, то его значение, вычисленное до последнего уровня вложенности, присваивается переменной. Ее имя выбирается из опции resultname, а при отсутствии опции – генерируется;

·       если eltra – список, массив или r-таблица выражений, то каждый их элемент вычисляется до последнего уровня вложенности, и полученный объект присваивается сгенерированной переменной. Ее имя выбирается из опции resultname, а при отсутствии опции – генерируется. Неинициализированным элементам при трансляции присваивается нулевое значение;

·       если eltra – список уравнений вида x = expr, где x – переменная, а
expr – выражение, то при трансляции генерируется последовательность присваиваний, соответствующих уравнениям в порядке их следования. При этом каждое выражение expr перед присваиванием вычисляется до последнего уровня вложенности;

·       если eltra – процедура, то генерируется эквивалентная ей в MATLAB функция;

·       если eltra – модуль, то фактически транслируются лишь все составляющие его процедуры. Выводу каждого экспортируемого члена модуля предшествует комментарий “%имя модуля/имя процедуры.m”, а выводу каждой локальной процедуры предшествует комментарий “%имя модуля/private/ имя процедуры.m”. Это позволяет, при необходимости, быстро создать иерархию папок и разместить в них m-файлы, соответствующие полученными при трансляции функциям.

        Пример 7.

> 

restart:

{¿}

> 

z := 7: a := x^2+y^2+z:

{Shift+¿}

 

CodeGeneration[Matlab](a);

{¿}

> 

cc := dd: aa := bb: bb := cc:

{Shift+¿}

 

xx := [a, b, c, aa]:

{Shift+¿}

 

CodeGeneration[Matlab](xx, resultname = ggg);

{¿}

> 

a := rtable(1..5): a[1] := 8: a[4] := 18:

{Shift+¿}

 

CodeGeneration[Matlab](a);

{¿}

> 

li := [x = a, y = b, z = c+d+y]:

{Shift+¿}

 

CodeGeneration[Matlab](li);

{¿}

        Пример 8. Не используя функции min, составить Maple-процедуру нахождения наименьшего элемента одномерного числового массива и транслировать ее в язык MATLAB.

        Решение. Первая из поставленных задач решается процедурой mi. Поиск ведется в массиве v типа Vector, передаваемого mi в качестве фактического параметра. Алгоритм поиска достаточно очевиден. В результате трансляции mi выведено предупреждающее сообщение “Warning, the function names … – не понято имя функции LinearAlgebra[Dimension]”, а также MATLAB-код в виде функции mi. Иными словами, транслятор не разобрался в том, как вычислена длина вектора v, и в выведенном коде соответствующую строку n := LinearAlgebra[Dimension](v) оставил без изменения. Если ее заменить строкой n = length(v) и исправленный код записать на диск в виде m-файла, например, mi.m, то мы получим отлаженную MATLAB-программу нахождения наименьшего элемента массива. В системе MATLAB по функции mi.m просчитан один контрольный пример. 

 

> 

Restart:

{¿}

> 

mi := proc(v::Vector)

{Shift+¿}

 

   local n, j, a;

{Shift+¿}

 

   n := LinearAlgebra[Dimension](v);

{Shift+¿}

 

   a := v[1]:

{Shift+¿}

 

   if n > 1 then

{Shift+¿}

 

      for j from 2 to n do

{Shift+¿}

 

         if v[j] < a then a := v[j] end if

{Shift+¿}

 

      end do

{Shift+¿}

 

   end if;

{Shift+¿}

 

   A

{Shift+¿}

 

end proc:

{¿}

 

 

 

> 

a := <1, 2, -7, 3, 0, 7.1, -6, -11, 9>: mi(a);

{¿}

> 

CodeGeneration[Matlab](mi);

{¿}

 

 

 

>> 

% после замены n = Linear … на n = length(v) и

 

>> 

% записи кода в MATLAB под именем mi.m имеем:

 

>> 

b = [7, 4, 6, 2, 1, 3, 8]; mi(b)  

 

        Замечание. Количество n элементов вектора v::Vector (или v::rtable) можно подсчитывать разными способами, отличными от предложенного в процедуре mi. Например, с помощью функций rtable_num_elems(v), op(1, v) или linalg[vectdim](v). Однако и они не будут распознаны транслятором. 

        При v::array или v::list количество элементов v равно linalg[vectdim](v). А во втором случае его можно считать и по nops(v).

        При трансляции рекурсивные процедуры конвертируются в рекурсивные программы. Из-за компактности получаемого кода его правка, как правило, труда не вызывает. А состоит она в замене не распознанных транслятором фрагментов кода подходящими средствами языка MATLAB. Следует иметь в виду, что транслятор не понимает, что он обрабатывает рекурсивную процедуру. Например, в выводимом предупреждающем сообщении имя процедуры в рекурсивных обращениях трактуется им как не понятое.

        Пример 9. Здесь составлен рекурсивный аналог mis процедуры mi предыдущего примера и проведена его трансляция. В результате выведено предупреждающее сообщение “Warning, the function names … – не поняты имена функций mis, LinearAlgebra:-Dimension и LinearAlgebra:-SubVector”, а также MATLAB-код в виде функции mis. Иными словами, транслятор не разобрался в том, как вычислена длина вектора v и как получен подвектор вектора v, и соответствующие фрагменты кода оставил без изменения. Если провести их замену по схеме:

LinearAlgebra:-Dimension(v); ® length(v);

LinearAlgebra:-SubVector(v, [2..n]); ® v(2:n);

и исправленный код записать на диск в виде m-файла, например, mis.m, то мы получим отлаженную рекурсивную MATLAB-программу нахождения наименьшего элемента массива.

        Обратите внимание на нотацию со знаком “:-”, используемую в mis для доступа к средствам пакета LinearAlgebra. Это допустимо, так как указанный пакет оформлен в виде модуля.

> 

restart:

{¿}

> 

mis := proc(v::Vector)

{Shift+¿}

 

   local n, a, b;

{Shift+¿}

 

   n := LinearAlgebra:-Dimension(v); a := v[1];

{Shift+¿}

 

   if n > 1 then

{Shift+¿}

 

      b := mis(LinearAlgebra:-SubVector(v, [2..n]));

{Shift+¿}

 

      if b < a then a := b end if;

{Shift+¿}

 

   end if; a

{Shift+¿}

 

end proc:

{¿}

> 

v := <1, 2, -7, 3, 0, -13, 7.1, -6, -11, 9>: mis(v);

{¿}

 

CodeGeneration:-Matlab(mis);

{¿}

Трансляция Maple-кода в язык C

        Функция С(eltra, opts) транслирует выражение, список, массив, r-таблицу, процедуру или модуль eltra из Maple в язык C стандарта ANSI. Это делается приблизительно так же, как и при трансляции из Maple в MATLAB. Необязательные аргументы opts – опции, помогающие процессу трансляции. К средствам, распознаваемым всеми трансляторами, в данном случае добавляются функции ceil, floor, if (функция), length, sprintf, system. Все математические функции транслируются в соответствующие функции стандартной библиотеки math.h языка C ANSI. Поскольку в этой библиотеке нет аналогов для функций max, min, round, sign и signum, то они реализуются условным оператором. Функции mod, modp и mods транслируются с использованием оператора % языка C, что не всегда удается сделать корректно. Степени выше второй конвертируются в функцию pow. Имена, недопустимые в C, подправляются автоматически. Индексы массивов преобразуются к принятому в C диапазону от 0 и далее.

        Пример 10. Здесь процедура mi примера 8 транслирована в язык C. Функция LinearAlgebra[Dimension] оказалась нераспознанной. Если заменить ее, то получим отлаженную программу нахождения наименьшего значения в числовом массиве на языке С.

> 

restart:

{¿}

> 

CodeGeneration:-C(mi);

{¿}

Трансляция Maple-кода в язык VisualBasic

        Функция VisualBasic(eltra, opts) транслирует выражение, список, массив, r-таблицу, процедуру или модуль eltra из Maple в язык VisualBasic. Для не процедур и не модулей это делается приблизительно так же, как и при трасляции из Maple в MATLAB. Необязательные аргументы opts – опции, помогающие процессу трансляции. Полученный код подходит и для  Visual Basic.NET. К средствам, распознаваемым всеми трансляторами, в данном случае добавляются функции cat, ceil и floor. Любые математические функции переводятся в соответствующие функции стандартной библиотеки System.Math языка Visual Basic. Поскольку в этой библиотеке нет аналогов для обратных гиперболических функций, то они реализуются через функции Log, Sqrt и Pow. Константа Pi конвертируется в System.Math.PI. Преобразование типов single в double не совершается. Предложение printf переводится в System.Console.WriteLine или System.Console.Write, что может привести к игнорированию при выводе некоторых инструкций форматирования. Индексы массивов преобразуются к диапазону от 0 и далее. При трансляции процедуры встраиваются в модули с именем CodeGenerationModule, которые легко включать в другие Basic-программы. Модули транслируются в модули. Их экспортные и локальные члены конвертируются соответственно в Public и Private функции, подпрограммы или переменные.

        Пример 11. Здесь процедура mi примера 8 транслирована в язык VisualBasic. Функция LinearAlgebra[Dimension] оказалась нераспознанной. Если заменить ее, то получим отлаженную программу нахождения наименьшего значения в числовом массиве на языке VisualBasic.

> 

restart:

{¿}

> 

CodeGeneration:-VisualBasic(mi);

{¿}

Трансляция Maple-кода в язык Java

        Функция Java(eltra, opts) транслирует выражение, список, массив, r-таблицу, процедуру или модуль eltra из Maple в язык Java 2. Для не процедур и не модулей это делается приблизительно так же, как и при трансляции из Maple в MATLAB. Необязательные аргументы opts – опции, помогающие процессу трансляции. К средствам, распознаваемым всеми трансляторами, в данном случае добавляются функции cat, ceil, floor и if (функция). Любые математические функции переводятся в соответствующие функции стандартной библиотеки java.lang.Math языка Java. Поскольку в этой библиотеке нет аналогов для гиперболических тригонометрических функций, то они реализуются через экспоненциальные функции. Отсутствующие в Java функции sign и signum реализуются условным оператором. Функции mod, modp и mods транслируются с использованием оператора % языка Java, что не всегда удается сделать корректно. Константа Pi конвертируется в Math.PI. Преобразование типов single в double не совершается. Предложение printf переводится в System.out, что может привести к игнорированию при выводе некоторых инструкций форматирования. Индексы массивов преобразуются к диапазону от 0 и далее. При трансляции процедур генерируются Java классы, содержащие эквивалентные этим процедурам функции. Модули транслируются в Java классы с членами типа static. Их экспортные и локальные члены конвертируются соответственно в Public методы или поля и Private методы или поля.

 

        Пример 13. Здесь процедура mi примера 8 транслирована в язык Java. Функция LinearAlgebra[Dimension] оказалась нераспознанной. Если заменить ее, то получим отлаженную программу нахождения наименьшего значения в числовом массиве на языке Java.

> 

restart:

{¿}

> 

CodeGeneration:-Java(mi);

{¿}

Задания:

1. Получить все меньшие 106 натуральные числа, которые являются палиндромами как в десятичной, так и в двоичной системах. Оптимизировать программу и транслировать в язык C.